﻿// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Azure.Provisioning.Generator.Model;

public class EnumModel : ModelBase
{
    private readonly List<EnumValue> _values = [];
    public IList<EnumValue> Values => _values;

    public EnumModel(Type armType, string name, string? ns = default, string? description = default)
        : base(name, ns, armType, description)
    {
        TypeRegistry.Register(this);
    }

    public override string ToString() => $"<Enum {Namespace}.{Name}>";

    public EnumValue AddValue(string name, string? value = default, string? description = default)
    {
        if (_values.Any(v => v.Name == name))
        {
            throw new ArgumentException($"Enum value {name} already exists in enum {Namespace}.{Name}!");
        }
        EnumValue val = new(this, name, value, description);
        _values.Add(val);
        return val;
    }

    public override void Lint()
    {
        base.Lint();
        if (Values.Count == 0) { Warn($"Enum {GetTypeReference()} has no values."); }
        foreach (EnumValue value in Values)
        {
            if (value.Name is null) { Warn($"Enum {value.Parent.GetTypeReference()} has a value with no {nameof(EnumValue.Name)}."); }
            if (value.Value is null) { Warn($"Enum value {value.Parent.GetTypeReference()}.{value.Name} has no {nameof(EnumValue.Value)}."); }
            //else if (value.Value.Contains(' ')) { Warn($"Enum value {value.Parent.GetTypeReference()}.{value.Name} has spaces: {value.Value}."); }
            //if (value.Description is null) { Warn($"Enum value {value.Parent.GetTypeReference()}.{value.Name} has no {nameof(EnumValue.Description)}."); }
        }
    }

    public override void Generate()
    {
        ContextualException.WithContext(
            $"Generating enum {Namespace}.{Name}",
            () =>
            {
                IndentWriter writer = new();
                writer.WriteLine("// Copyright (c) Microsoft Corporation. All rights reserved.");
                writer.WriteLine("// Licensed under the MIT License.");
                writer.WriteLine();
                writer.WriteLine("// <auto-generated/>");
                writer.WriteLine();
                if (Values.Any(v => v.Name != v.Value || v.Hidden))
                {
                    if (Values.Any(v => v.Name != v.Value))
                    {
                        writer.WriteLine($"using System.Runtime.Serialization;");
                    }
                    if (Values.Any(v => v.Hidden))
                    {
                        writer.WriteLine($"using System.ComponentModel;");
                    }
                    writer.WriteLine();
                }
                writer.WriteLine($"namespace {Namespace};");
                writer.WriteLine();
                writer.WriteLine($"/// <summary>");
                writer.WriteWrapped(Description ?? $"{Name} values.");
                writer.WriteLine($"/// </summary>");
                writer.WriteLine($"public enum {Name}");
                using (writer.Scope("{", "}"))
                {
                    // Write the values
                    var fence = new IndentWriter.Fenceposter();
                    foreach (EnumValue value in Values)
                    {
                        if (fence.RequiresSeparator) { writer.WriteLine(); }
                        writer.WriteLine($"/// <summary>");
                        writer.WriteWrapped(value.Description ?? value.Value ?? value.Name);
                        writer.WriteLine($"/// </summary>");
                        if (value.Hidden)
                        {
                            writer.WriteLine("[EditorBrowsable(EditorBrowsableState.Never)]");
                        }
                        if (value.Name != value.Value)
                        {
                            writer.WriteLine($"[DataMember(Name = \"{value.Value}\")]");
                        }
                        writer.WriteLine($"{value.Name},");
                    }
                }

                // TODO: Decide if we want Serializer helper methods for end users rather than
                // relying on [DataMember]

                //writer.WriteLine();
                //writer.WriteLine($"public static partial class {Namespace!.Split('.').Last()}Extensions");
                //using (writer.Scope("{", "}"))
                //{
                //    writer.WriteLine($"/// <summary>");
                //    writer.WriteLine($"/// Serializes a {Name} value.");
                //    writer.WriteLine($"/// </summary>");
                //    writer.WriteLine($"/// <param name=\"value\">The value to serialize.</param>");
                //    writer.WriteLine($"public static string Serialize(this {Name} value) =>");
                //    using (writer.Scope())
                //    {
                //        writer.WriteLine("value switch");
                //        using (writer.Scope("{", "};"))
                //        {
                //            foreach (EnumValue value in Values)
                //            {
                //                writer.WriteLine($"{Name}.{value.Name} => \"{value.Value ?? value.Name}\",");
                //            }
                //            writer.WriteLine($"_ => throw new ArgumentOutOfRangeException(nameof(value), value, \"Unknown {Name} value: \" + value)");
                //        }
                //    }
                //}

                // Write out the enum
                Spec!.SaveFile(Path.Combine("Models", $"{Name}.cs"), writer.ToString());
            });
    }
}

public class EnumValue(EnumModel parent, string name, string? value = default, string? description = default)
{
    public EnumModel Parent { get; } = parent;
    public string Name { get; set; } = name;
    public string? Value { get; set; } = value;
    public string? Description { get; set; } = description;
    public bool Hidden { get; set; } = false;

    public override string ToString() => $"<EnumValue {Parent.Namespace}.{Parent.Name}::{Name}>";
}
